﻿
using DG.Tweening;
using Nirvana;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using UnityEditor;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.Audio;


namespace Nirvana.SoundCommon
{
    /// <summary>
    /// 通用音效框架
    /// </summary>
    [DisallowMultipleComponent]
    public class AudioManager : UnitySingleton<AudioManager>
    {
        private bool isMuteMusic, isMuteSound;
        private AudioSourcePool audioPool;
        public AudioSourcePool AudioPool => audioPool;
        //记录当前播放的音效
        public List<IPlayerAudio> playerAudioList = new List<IPlayerAudio>();
        private Dictionary<string, AudioItem> audioItemMap = new Dictionary<string, AudioItem>();
        private Dictionary<AudioItemMixerGroup, AudioMixerGroupItem> audioMixerItemMap = new Dictionary<AudioItemMixerGroup, AudioMixerGroupItem>();
        //数据结构缓存，避免大量new gc
        private Queue<AudioSubItemData> cacheItemData = new Queue<AudioSubItemData>();
        private Predicate<IPlayerAudio> _freeJudge;
        //记录当前播放的背景音效名
        private string CurPlayBGMName = "";
        // 音频混合器
        private AudioMixer MyAudioMixer;
        public AudioMixerGroup MusicGroup;
        public AudioMixerGroup UISoundGroup;
        public AudioMixerGroup FuncSoundGroup;
        public AudioMixerGroup EffectSoundGroup;
        public AudioMixerGroup DieBoomSoundGroup;
        private void LoadMixer()
        {
            //默认保存Resources目录下，快速加载，避免下载
            var result = Resources.Load<AudioMixer>("Audio/AudioMixer/CommonMainAudioMixer");
            if (result != null)
            {
                MyAudioMixer = result;
                AudioMixerGroup[] groups = MyAudioMixer.FindMatchingGroups("Master");
                foreach (AudioMixerGroup group in groups)
                {
                    if (group.name == "MusicGroup")
                    {
                        MusicGroup = group;
                    }
                    if (group.name == "UISoundGroup")
                    {
                        UISoundGroup = group;
                    }
                    if (group.name == "FuncSoundGroup")
                    {
                        FuncSoundGroup = group;
                    }
                    if (group.name == "EffectSoundGroup")
                    {
                        EffectSoundGroup = group;
                    }
                    if (group.name == "DieBoomSoundGroup")
                    {
                        DieBoomSoundGroup = group;
                    }
                }
            }
        }

        private void Awake()
        {
            if (audioPool == null)
            {
                GameObject obj = new GameObject("Audio Source Pool");
                obj.transform.parent = transform;
                this.audioPool = new AudioSourcePool(transform, obj.transform);
            }

#if UNITY_EDITOR
            if (!Application.isPlaying)
            {
                EditorApplication.update += this.LateUpdate;
            }
#endif
            LoadMixer();

            _freeJudge = OnRemove;
        }

        /// <summary>
        /// 每帧更新音效，不受scale影响
        /// </summary>
        private void LateUpdate()
        {
            if (_freeJudge != null && playerAudioList !=  null)
                playerAudioList.RemoveAll(_freeJudge);
        }

        private static bool OnRemove(IPlayerAudio audio)
        {
            if (audio.IsPlaying)
            {
                audio.Update();
                return false;
            }
            audio.Remove();
            return true;
        }

        protected override void OnDispose()
        {
#if UNITY_EDITOR
            if (!Application.isPlaying)
            {
                EditorApplication.update -= this.LateUpdate;
            }
#endif
            StopAllCoroutines();
            _freeJudge = null;

            if (playerAudioList != null)
            {
                this.Stop();
                foreach (var p in this.playerAudioList)
                {
                    p.Dispose();
                }
                playerAudioList.Clear();
                playerAudioList = null;
            }

            if(audioItemMap != null)
            {
                foreach (var p in this.audioItemMap)
                {
                    p.Value?.Dispose();
                }
                audioItemMap.Clear();
            }
            audioItemMap = null;

            cacheItemData.Clear(); cacheItemData = null;

            if(audioMixerItemMap != null)
            {
                foreach (var p in this.audioMixerItemMap)
                {
                    p.Value?.Dispose();
                }
                audioMixerItemMap.Clear(); audioMixerItemMap = null;
            }
        }

        public AudioSubItemData PopAudioItem()
        {
            if (Instance == null)
            {
                return null;
            }
            if (cacheItemData.Count > 0)
                return cacheItemData.Dequeue();
            else 
                return null;
        }

        public void PushAudioItem(AudioSubItemData s)
        {
            if (Instance == null)
            {
                return;
            }
            if(cacheItemData.Count <= 20) cacheItemData.Enqueue(s);

        }
        public static void StopAll()
        {
            if (Instance == null)
            {
                return;
            }
            Instance.Stop();
        }

        public static void Stop(string soundname)
        {
            foreach (var p in Instance.playerAudioList)
            {
                var s = p.GetAudioName();
                if (string.IsNullOrEmpty(s)) continue;
                if (s.Equals(soundname))
                {
                    p.Stop();
                    return;
                }
            }
        }

        private void Stop()
        {
            foreach (var p in this.playerAudioList)
            {
                p.Stop();
            }
        }

        private void SetMute()
        {
            foreach (var p in this.playerAudioList)
            {
                p.Mute(isMuteMusic);
            }
        }


        private void SetSoundGroupVolume(float volume, float fadetime, bool isNeedAni = true)
        {
            if (UISoundGroup != null)
            {
                if (isNeedAni)
                    UISoundGroup.audioMixer.DOSetFloat("UISoundVolume", volume, fadetime);
                else
                    UISoundGroup.audioMixer.SetFloat("UISoundVolume", volume);
            }
            if (FuncSoundGroup != null)
            {
                if (isNeedAni)
                    FuncSoundGroup.audioMixer.DOSetFloat("FuncSoundVolume", volume, fadetime);
                else
                    FuncSoundGroup.audioMixer.SetFloat("FuncSoundVolume", volume);
            }
            if (EffectSoundGroup != null)
            {
                float _v = volume;// 0 ? -5 : volume;
                if (isNeedAni)
                    EffectSoundGroup.audioMixer.DOSetFloat("EffectSoundVolume", _v, fadetime);
                else
                    EffectSoundGroup.audioMixer.SetFloat("EffectSoundVolume", _v);
            }
            if (DieBoomSoundGroup != null)
            {
                float _v = volume == 0 ? -5 : volume;
                if (isNeedAni)
                    DieBoomSoundGroup.audioMixer.DOSetFloat("DieBoomSoundVolume", _v, fadetime);
                else
                    DieBoomSoundGroup.audioMixer.SetFloat("DieBoomSoundVolume", _v);
            }
        }

        /// <summary>
        /// 停止音乐
        /// </summary>
        /// <param name="isStop"></param>
        public static void StopMusic(bool isStop, bool isNeedAni = true)
        {

            if (Instance == null)
            {
                return;
            }

            Instance.isMuteMusic = isStop;

            if (Instance.MusicGroup != null)
            {
                if (isStop)
                {
                    if (isNeedAni)
                        Instance.MusicGroup.audioMixer.DOSetFloat("MusicVolume", -80, 3);
                    else
                        Instance.MusicGroup.audioMixer.SetFloat("MusicVolume", -80);
                    Instance.PauseBGM();
                }
                else
                {
                    if (isNeedAni)
                        Instance.MusicGroup.audioMixer.DOSetFloat("MusicVolume", -2, 2);
                    else
                        Instance.MusicGroup.audioMixer.SetFloat("MusicVolume", -2);

                    if (!Instance.ResumeBGM())
                    {
                        if (!string.IsNullOrEmpty(Instance.CurPlayBGMName)) PlayBGM(Instance.CurPlayBGMName);
                    }
                }

            }

        }

        /// <summary>
        /// 设置音乐音量
        /// </summary>
        /// <param name="isStop"></param>
        public static void SetMusicVolume(float value)
        {
            if (Instance == null)
            {
                return;
            }
            if (Instance.MusicGroup != null)
            {
                Instance.MusicGroup.audioMixer.SetFloat("MusicVolume", value);
            }
        }

        /// <summary>
        /// 停止音效
        /// </summary>
        public static void StopSound(bool isStop, bool isNeedAni = true)
        {
            if (Instance == null)
            {
                return;
            }
            Instance.isMuteSound = isStop;
            if (isStop)
            {
                Instance.SetSoundGroupVolume(-80, 3, isNeedAni);
            }
            else
            {
                Instance.SetSoundGroupVolume(0, 2, isNeedAni);
            }

        }

        /// <summary>
        /// 设置音效音量
        /// </summary>
        /// <param name="isStop"></param>
        public static void SetSoundVolume(float value)
        {
            if (Instance == null)
            {
                return;
            }
            if (Instance.MusicGroup != null)
            {
                Instance.SetSoundGroupVolume(value, 0.1f);
            }
        }
        /// <summary>
        /// 恢复暂停的背景音乐
        /// </summary>
        /// <returns></returns>
        public bool ResumeBGM()
        {
            foreach (var p in Instance.playerAudioList)
            {
                if (p.GetMixerGroupType() == AudioItemMixerGroup.MusicGroup)
                {
                    p?.Mute(false);
                    p?.Play();
                    return true;
                }
            }

            return false;
        }

        public void PauseBGM()
        {
            foreach (var p in Instance.playerAudioList)
            {
                if (p.GetMixerGroupType() == AudioItemMixerGroup.MusicGroup)
                {
                    p?.Mute(true);
                }
            }
        }
        /// <summary>
        /// 播放背景音乐
        /// </summary>
        /// <param name="audioname"></param>
        public static void PlayBGM(string audioname)
        {
            if (Instance == null || Instance.MyAudioMixer == null) return;
            if (Instance.CurPlayBGMName == audioname) return;
            Instance.CurPlayBGMName = audioname;

            if (Instance.isMuteMusic) return;
            var config = SoundDataManager.Instance.GetSoundConfig(audioname);
            if (config.Id == 0)
            {
                Debug.LogErrorFormat("Dont Play Sound :{0} , Please Check Sound Config", audioname);
                return;
            }

            //前背景音乐淡出
            foreach (var p in Instance.playerAudioList)
            {
                if (p.GetMixerGroupType() == AudioItemMixerGroup.MusicGroup)
                {
                    p?.Stop();
                    //Instance.playerAudioList.Remove(p);
                    break;
                }
            }

            var assetid = GetAssetBundleName(config.resUrl);
            Instance.PlayAndForget(assetid);
        }
        /// <summary>
        /// 播放音效
        /// </summary>
        /// <param name="audioname"></param>
        public static void Play(string audioname)
        {
            if (Instance == null || Instance.isMuteSound || Instance.MyAudioMixer == null || string.IsNullOrEmpty(audioname)) return;
            var config = SoundDataManager.Instance.GetSoundConfig(audioname);
            if (config.Id == 0)
            {
                Debug.LogErrorFormat("Dont Play Sound :{0} , Please Check Sound Config", audioname);
                return;
            }

            var assetid = GetAssetBundleName(config.resUrl);
            if (Instance.audioItemMap.ContainsKey(assetid.AssetName))
            {
                var audioItem = Instance.audioItemMap[assetid.AssetName];
                if (!audioItem.IsCanPlay())
                {
                    Debug.LogFormat("{0} Sound is Playing...",assetid.AssetName);
                    return;
                }
            }
            Instance.PlayAndForget(assetid);
        }

        #region 播放已加载的音效资源
#if false
        private static IBasicAudio Play(AudioItem item,AudioSubItemData subItemData)
        {
            return Play(item, subItemData, Vector3.zero);
        }

        private static IBasicAudio Play(AudioItem item, AudioSubItemData subItemData, Vector3 pos)
        {
            Assert.IsNotNull<AudioItem>(item);
            if (Instance == null || item == null) return PlayerAudioBasic.Instance;
            var audio = item.GetPlayerAudio(Instance.audioPool, subItemData);
            audio.Set(pos);
            audio.Play();
            audio.Mute(false);
            Instance.playerAudioList.Add(audio);
            return audio;
        }

        private static IBasicAudio Play(AudioItem item, AudioSubItemData subItemData, Transform parent)
        {
            Assert.IsNotNull<AudioItem>(item);
            if (Instance == null || item == null) return PlayerAudioBasic.Instance;
            var audio = item.GetPlayerAudio(Instance.audioPool, subItemData);
            audio.Set(parent);
            audio.Play();
            audio.Mute(false);
            Instance.playerAudioList.Add(audio);

            return audio;
        }
        private static void PlayAndForget(AudioItem item, AudioSubItemData subItemData)
        {
            PlayAndForget(item, subItemData, Vector3.zero);
        }
        private static void PlayAndForget(AudioItem item, AudioSubItemData subItemData, Vector3 position)
        {
            Assert.IsNotNull<AudioItem>(item);
            if (Instance == null || item == null) return;
            var audio = item.GetPlayerAudio(Instance.audioPool, subItemData);
            audio.Set(position);
            audio.Play();
            audio.Mute(false);
            Instance.playerAudioList.Add(audio);
        }

        private static void PlayAndForget(AudioItem item, AudioSubItemData subItemData, Transform parent)
        {
            Assert.IsNotNull<AudioItem>(item);
            if (Instance == null || item == null) return;
            var audio = item.GetPlayerAudio(Instance.audioPool, subItemData);
            audio.Set(parent);
            audio.Play();
            audio.Mute(false);
            Instance.playerAudioList.Add(audio);
        }
#endif
        #endregion

        #region 下载音效资源并播放
        private void PlayAndForget(string bundle, string asset)
        {
            PlayAndForget(new AssetID(bundle, asset));
        }

        private void PlayAndForget(string bundle, string asset, Vector3 position)
        {
            PlayAndForget(new AssetID(bundle, asset), position);
        }
        private void PlayAndForget(string bundle, string asset, Transform parent)
        {
            PlayAndForget(new AssetID(bundle, asset), parent);
        }
        private void PlayAndForget(AssetID assetid)
        {

            StartCoroutine(WaitPlay(assetid));

        }

        private void PlayAndForget(AssetID assetID, Vector3 position)
        {

            StartCoroutine(WaitPlay(assetID, position));
        }

        private void PlayAndForget(AssetID assetID, Transform parent)
        {
            StartCoroutine(WaitPlay(assetID, parent));
        }


        private IEnumerator WaitPlay(AssetID assetid, Vector3 pos)
        {
            var waitLoad = AudioClipPool.Instance.Load(assetid);
            yield return waitLoad;
            if (!string.IsNullOrEmpty(waitLoad.Error))
            {
                Debug.LogErrorFormat("PlayAndForget {0} failed: {1}", assetid, waitLoad.Error);
                yield break;
            }

            var audioclip = waitLoad.LoadedObject;

            AudioItem audioItem = null;
            if (audioItemMap.ContainsKey(assetid.AssetName))
            {
                audioItem = audioItemMap[assetid.AssetName];
            }
            else
            {
                audioItem = new AudioItem();
                audioItem.Init(assetid.AssetName);
                audioItemMap.Add(assetid.AssetName, audioItem);
            }

            audioItem.Play(audioclip,assetid.AssetName);
            
            yield break;
        }

        private IEnumerator WaitPlay(AssetID assetid, Transform parent)
        {
            var waitLoad = AudioClipPool.Instance.Load(assetid);
            yield return waitLoad;
            if (!string.IsNullOrEmpty(waitLoad.Error))
            {
                Debug.LogErrorFormat("PlayAndForget {0} failed: {1}", assetid, waitLoad.Error);
                yield break;
            }
            var audioclip = waitLoad.LoadedObject;

            AudioItem audioItem = null;
            if (audioItemMap.ContainsKey(assetid.AssetName))
            {
                audioItem = audioItemMap[assetid.AssetName];
            }
            else
            {
                audioItem = new AudioItem();
                audioItem.Init(assetid.AssetName);
                audioItemMap.Add(assetid.AssetName, audioItem);
            }

            audioItem.Play(audioclip, assetid.AssetName);

            yield break;
        }
        private IEnumerator WaitPlay(AssetID assetid)
        {
            var waitLoad = AudioClipPool.Instance.Load(assetid);
            yield return waitLoad;
            if (!string.IsNullOrEmpty(waitLoad.Error))
            {
                Debug.LogErrorFormat("PlayAndForget {0} failed: {1}", assetid, waitLoad.Error);
                yield break;
            }
            var audioclip = waitLoad.LoadedObject;

            AudioItem audioItem = null;
            if (audioItemMap.ContainsKey(assetid.AssetName))
            {
                audioItem = audioItemMap[assetid.AssetName];
            }
            else
            {
                audioItem = new AudioItem();
                audioItem.Init(assetid.AssetName);
                audioItemMap.Add(assetid.AssetName, audioItem);
            }

            audioItem.Play(audioclip, assetid.AssetName);

            yield break;
        }
        #endregion


        private static AssetID GetAssetBundleName(string prefabPath)
        {
            AssetID assetid = new AssetID();
            int lastindex = prefabPath.LastIndexOf("/");
            var resName = prefabPath.Substring(lastindex + 1);
            var abName = prefabPath.Substring(0, lastindex);

            abName = abName.Replace('/', '.');
            abName = string.Concat(abName.ToLower(), ".assetbundle");
            assetid.BundleName = abName;
            assetid.AssetName = resName;
            return assetid;
        }

        public AudioMixerGroupItem GetAudioMixerGroup(AudioItemMixerGroup mixer)
        {
            if (audioMixerItemMap.ContainsKey(mixer))
            {
                return audioMixerItemMap[mixer];
            }
            else
            {
                var audioMixerGroupItem = new AudioMixerGroupItem(mixer);
                audioMixerItemMap.Add(mixer, audioMixerGroupItem);
                return audioMixerGroupItem;
            }
        }

    }
}
